/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.gitee.vueantd.common.utils;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gitee.vueantd.common.utils.FileUtil;
import com.gitee.vueantd.common.utils.SpringContextHolder;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;

/**
 * @author Zheng Jie
 * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {

  private static final Logger log = LoggerFactory.getLogger(StringUtils.class);
  private static boolean ipLocal = false;
  private static File file = null;
  private static DbConfig config;
  private static final char SEPARATOR = '_';
  private static final String UNKNOWN = "unknown";

  static {
    SpringContextHolder.addCallBacks(() -> {
      StringUtils.ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class);
      if (ipLocal) {
        /*
         * 此文件为独享 ，不必关闭
         */
        String path = "ip2region/ip2region.db";
        String name = "ip2region.db";
        try {
          config = new DbConfig();
          file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name);
        } catch (Exception e) {
          log.error(e.getMessage(), e);
        }
      }
    });
  }

  /**
   * 驼峰命名法工具
   *
   * @return toCamelCase(" hello_world ") == "helloWorld"
   * toCapitalizeCamelCase("hello_world") == "HelloWorld"
   * toUnderScoreCase("helloWorld") = "hello_world"
   */
  public static String toCamelCase(String s) {
    if (s == null) {
      return null;
    }

    s = s.toLowerCase();

    StringBuilder sb = new StringBuilder(s.length());
    boolean upperCase = false;
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);

      if (c == SEPARATOR) {
        upperCase = true;
      } else if (upperCase) {
        sb.append(Character.toUpperCase(c));
        upperCase = false;
      } else {
        sb.append(c);
      }
    }

    return sb.toString();
  }

  /**
   * 驼峰命名法工具
   *
   * @return toCamelCase(" hello_world ") == "helloWorld"
   * toCapitalizeCamelCase("hello_world") == "HelloWorld"
   * toUnderScoreCase("helloWorld") = "hello_world"
   */
  public static String toCapitalizeCamelCase(String s) {
    if (s == null) {
      return null;
    }
    s = toCamelCase(s);
    return s.substring(0, 1).toUpperCase() + s.substring(1);
  }

  /**
   * 驼峰命名法工具
   *
   * @return toCamelCase(" hello_world ") == "helloWorld"
   * toCapitalizeCamelCase("hello_world") == "HelloWorld"
   * toUnderScoreCase("helloWorld") = "hello_world"
   */
  static String toUnderScoreCase(String s) {
    if (s == null) {
      return null;
    }

    StringBuilder sb = new StringBuilder();
    boolean upperCase = false;
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);

      boolean nextUpperCase = true;

      if (i < (s.length() - 1)) {
        nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
      }

      if ((i > 0) && Character.isUpperCase(c)) {
        if (!upperCase || !nextUpperCase) {
          sb.append(SEPARATOR);
        }
        upperCase = true;
      } else {
        upperCase = false;
      }

      sb.append(Character.toLowerCase(c));
    }

    return sb.toString();
  }

  /**
   * 获取ip地址
   */
  public static String getIp(HttpServletRequest request) {
    String ip = request.getHeader("x-forwarded-for");
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
      ip = request.getRemoteAddr();
    }
    String comma = ",";
    String localhost = "127.0.0.1";
    if (ip.contains(comma)) {
      ip = ip.split(",")[0];
    }
    if (localhost.equals(ip)) {
      // 获取本机真正的ip地址
      try {
        ip = InetAddress.getLocalHost().getHostAddress();
      } catch (UnknownHostException e) {
        log.error(e.getMessage(), e);
      }
    }
    return ip;
  }

  /**
   * 根据ip获取详细地址
   */
  public static String getCityInfo(String ip) {
    if (ipLocal) {
      return getLocalCityInfo(ip);
    } else {
      return getHttpCityInfo(ip);
    }
  }

  /**
   * 根据ip获取详细地址
   */
  public static String getHttpCityInfo(String ip) {
    String api = String.format(ElAdminConstant.Url.IP_URL, ip);
    JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
    return object.get("addr", String.class);
  }

  /**
   * 根据ip获取详细地址
   */
  public static String getLocalCityInfo(String ip) {
    try {
      DataBlock dataBlock = new DbSearcher(config, file.getPath())
              .binarySearch(ip);
      String region = dataBlock.getRegion();
      String address = region.replace("0|", "");
      char symbol = '|';
      if (address.charAt(address.length() - 1) == symbol) {
        address = address.substring(0, address.length() - 1);
      }
      return address.equals(ElAdminConstant.REGION) ? "内网IP" : address;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return "";
  }

  public static String getBrowser(HttpServletRequest request) {
    UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
    Browser browser = userAgent.getBrowser();
    return browser.getName();
  }

  /**
   * 获得当天是周几
   */
  public static String getWeekDay() {
    String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    Calendar cal = Calendar.getInstance();
    cal.setTime(new Date());

    int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
    if (w < 0) {
      w = 0;
    }
    return weekDays[w];
  }

  /**
   * 获取当前机器的IP
   *
   * @return /
   */
  public static String getLocalIp() {
    try {
      InetAddress candidateAddress = null;
      // 遍历所有的网络接口
      for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) {
        NetworkInterface anInterface = interfaces.nextElement();
        // 在所有的接口下再遍历IP
        for (Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) {
          InetAddress inetAddr = inetAddresses.nextElement();
          // 排除loopback类型地址
          if (!inetAddr.isLoopbackAddress()) {
            if (inetAddr.isSiteLocalAddress()) {
              // 如果是site-local地址，就是它了
              return inetAddr.getHostAddress();
            } else if (candidateAddress == null) {
              // site-local类型的地址未被发现，先记录候选地址
              candidateAddress = inetAddr;
            }
          }
        }
      }
      if (candidateAddress != null) {
        return candidateAddress.getHostAddress();
      }
      // 如果没有发现 non-loopback地址.只能用最次选的方案
      InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
      if (jdkSuppliedAddress == null) {
        return "";
      }
      return jdkSuppliedAddress.getHostAddress();
    } catch (Exception e) {
      return "";
    }
  }
}
